package martin.tpfinal.grabar.grafico;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

/**
 * Write data to the OutputChannel.
 */
public class Reproducir implements Runnable {

	private SourceDataLine line;
	private Thread thread;

	private VentanaPrincipal ventana;
 
	public Reproducir(VentanaPrincipal ventana){
		this.ventana=ventana;
	}
	
	public void start() {
		ventana.setErrStr(null);
		thread = new Thread(this);
		thread.setName("Playback");
		thread.start();
	}

 
	public void stop() {
		thread = null;
	}
        
	public SourceDataLine getLine() {
		return line;
	}

	public void setLine(SourceDataLine line) {
		this.line = line;
	}

	public Thread getThread() {
		return thread;
	}

	public void setThread(Thread thread) {
		this.thread = thread;
	}

	private void shutDown(String message) {
		ventana.setErrStr(message);
		if (ventana.getErrStr() != null) {
			System.err.println(ventana.getErrStr());
			ventana.getSamplingGraph().repaint();
		}
		if (thread != null) {
			thread = null;
			ventana.getSamplingGraph().stop();
			ventana.getCaptB().setEnabled(true);
			ventana.getPausB().setEnabled(false);
		} 
	}


	public void run() {

		// recargo el archivo si este fue cargado desde un archivo
		if (ventana.getFile() != null) {
			ventana.createAudioInputStream(ventana.getFile(), false);
		}

		// nos aseguramos que tenemos algo para reproducir
		if (ventana.getAudioInputStream() == null) {
			shutDown("No loaded audio to play back");
			return;
		}
		// reset to the beginnning of the stream
		try {
			ventana.getAudioInputStream().reset();
		} catch (Exception e) {
			shutDown("Unable to reset the stream\n" + e);
			return;
		}

		// get an AudioInputStream of the desired format for playback
		AudioFormat format = ventana.getFormatControls().getFormat();
		AudioInputStream playbackInputStream = AudioSystem.getAudioInputStream(format, ventana.getAudioInputStream());
                        
		if (playbackInputStream == null) {
			shutDown("No se puede convertir el stream de formato " + ventana.getAudioInputStream() + " al formato " + format);
			return;
		}

		// define the required attributes for our line, 
		// and make sure a compatible line is supported.
		DataLine.Info info = new DataLine.Info(SourceDataLine.class,format);
		if (!AudioSystem.isLineSupported(info)) {
			shutDown("Line matching " + info + " not supported.");
			return;
		}

		// get and open the source data line for playback.
		try {
			line = (SourceDataLine) AudioSystem.getLine(info);
			line.open(format, ventana.getBufSize());
		} catch (LineUnavailableException ex) { 
			shutDown("Unable to open the line: " + ex);
			return;
		}

		// play back the captured audio data
		int frameSizeInBytes = format.getFrameSize();
		int bufferLengthInFrames = line.getBufferSize() / 8;
		int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
		byte[] data = new byte[bufferLengthInBytes];
		int numBytesRead = 0;

		// start the source data line
		line.start();

		while (thread != null) {
			try {
				if ((numBytesRead = playbackInputStream.read(data)) == -1) {
					break;
				}
				int numBytesRemaining = numBytesRead;
				while (numBytesRemaining > 0 ) {
					numBytesRemaining -= line.write(data, 0, numBytesRemaining);
				}
			} catch (Exception e) {
				shutDown("Error during playback: " + e);
				break;
			}
		}
		// we reached the end of the stream.  let the data play out, then
		// stop and close the line.
		if (thread != null) {
			line.drain();
		}
		line.stop();
		line.close();
		line = null;
		shutDown(null);
	}
 
}
